

#include <cassert>
#include <windows.h>
#include <shlwapi.h>
#include <string>
#include "web_server.h"
#include "const.h"
#include "HttpRequest.h"
#include "HttpReply.h"
#include "file_http_reply.h"
#include "hello_world_reply.h"
#include "auto_index_reply.h"
#include "error_reply.h"
//#include "service-func.h"
#include "sock/server_socket.h"
#include "sock/text_socket.h"
#include "sock/selector.h"
#include "fs/path.h"

struct WebServer::Impl {

	// function-members.
	
	void handle_request( sock::TextSocket * psock );
	void handle_get( const HttpRequest &request, HttpReply &reply );
	HttpReply * prepare_reply( const HttpRequest & request );

	// members.
	static DWORD WINAPI worker( LPVOID pParam );
	Params params;
	MimeTypes mimetypes;
	sock::ServerSocket srvsock;
	std::vector<sock::TextSocket*> client_socks;
};

void WebServer::run()
{
	assert( _pimpl != NULL );

	_pimpl->srvsock.bind( sock::InetAddr( _pimpl->params.host, _pimpl->params.port ) );
	_pimpl->srvsock.listen();

	sock::Selector selector;
	selector.set( _pimpl->srvsock );
	int ready_socks_num = 0;
	while( ( ready_socks_num = selector.select() ) > 0 ){
		if( selector.isset( _pimpl->srvsock ) ){
			sock::TextSocket *pnewsock = new sock::TextSocket( _pimpl->srvsock.accept() );
			_pimpl->client_socks.push_back( pnewsock );
			selector.set( *pnewsock );
			--ready_socks_num;
		}
		for( size_t i=0; i<_pimpl->client_socks.size() && ready_socks_num > 0; i++ ){
			sock::TextSocket *psock = _pimpl->client_socks[i];
			if( selector.isset( *psock ) ){
				--ready_socks_num;				
				selector.erase( *psock );
				try {
					_pimpl->handle_request( _pimpl->client_socks[i] );
					if( psock->isconnected() ){
						selector.set( *psock );
					}

				} catch ( SocketError & e )	{
					printf( "error: %s", e.what() );
				}		
			}
		}
	}
}

void WebServer::configure(const Params &params, const MimeTypes &mimetypes)
{
	_pimpl->params = params;
	_pimpl->mimetypes = mimetypes;
}

void WebServer::reconfigure(const Params &params, const MimeTypes &mimetypes)
{
	// @todo some checks for stopless reconfiguration.

	configure( params, mimetypes );
}

WebServer::WebServer(const Params &params, const MimeTypes &mimetypes): _pimpl( new Impl )
{
	configure( params, mimetypes );
}

WebServer::~WebServer()
{
	delete _pimpl;
}


void WebServer::Impl::handle_request( sock::TextSocket * psock )
{
	std::string request_str, line;
	do {
		psock->readln( &line );
		request_str += line + _T("\n");
	} while ( !line.empty() );

	HttpRequest request;
	request.parse( request_str );
	printf( "%s\n", request_str.c_str() ); 
	std::auto_ptr<HttpReply> reply( prepare_reply( request ) );
	if( params.nocache )
	{
		reply->headers.set( "Cache-Control", "no-cache" );
	}
	reply->send( psock );

}

HttpReply* WebServer::Impl::prepare_reply( const HttpRequest & request )
{
	using fs::Path;
	Path fullpath = params.root + Path( request.resource );
	if( fullpath.is_directory() )
	{
		Path index_fullpath = fullpath + params.index ;
		if( index_fullpath.is_exist() )
		{
			return new FileHttpReply( index_fullpath, mimetypes );
		} 
		else 
		{
			if( params.autoindex )
			{
				return new AutoIndexReply( fullpath );
			}
			else 
			{
				return new ErrorReply( SC_FORBIDDEN );
			}
		}

	} 
	else 
	{
		return new FileHttpReply( fullpath, mimetypes );
	}

}